package cn.chinaunicom.fast.common.utils;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * Created by Administrator on 2016/2/25.
 */
public class IDCardUtils<main> {
	/** 中国公民身份证号码最小长度。 */
	public final int CHINA_ID_MIN_LENGTH = 15;

	/** 中国公民身份证号码最大长度。 */
	public final int CHINA_ID_MAX_LENGTH = 18;

	// 身份证校验码
	private static final int[] COEFFICIENT_ARRAY = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };

	// 身份证号的尾数规则
	private static final String[] IDENTITY_MANTISSA = { "1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2" };

	private static final String IDENTITY_PATTERN = "^[0-9]{17}[0-9Xx]$";

	// 秘钥数字
	private static final int SECRET_NO = 1314;

	/**
	 * 根据身份编号获取年龄
	 *
	 * @param idCard 身份编号
	 * @return 年龄
	 */
	public static int getAgeByIdCard(String idCard) {
		int iAge = 0;
		Calendar cal = Calendar.getInstance();
		String year = idCard.substring(6, 10);
		int iCurrYear = cal.get(Calendar.YEAR);
		iAge = iCurrYear - Integer.valueOf(year);
		return iAge;
	}

	/**
	 * 根据身份编号获取生日
	 *
	 * @param idCard 身份编号
	 * @return 生日(yyyyMMdd)
	 */
	public static String getBirthByIdCard(String idCard) {
		return idCard.substring(6, 14);
	}

	/**
	 * 根据身份编号获取生日年
	 *
	 * @param idCard 身份编号
	 * @return 生日(yyyy)
	 */
	public static Short getYearByIdCard(String idCard) {
		return Short.valueOf(idCard.substring(6, 10));
	}

	/**
	 * 根据身份编号获取生日月
	 *
	 * @param idCard 身份编号
	 * @return 生日(MM)
	 */
	public static Short getMonthByIdCard(String idCard) {
		return Short.valueOf(idCard.substring(10, 12));
	}

	/**
	 * 根据身份编号获取生日天
	 *
	 * @param idCard 身份编号
	 * @return 生日(dd)
	 */
	public static Short getDateByIdCard(String idCard) {
		return Short.valueOf(idCard.substring(12, 14));
	}

	public static String getBirthDayByIdCard(String idCard) {
		Short nian = getYearByIdCard(idCard);
		Short yue = getMonthByIdCard(idCard);
		Short ri = getDateByIdCard(idCard);
		return nian + "-" + yue + "-" + ri;
	}

	/**
	 * 根据身份编号获取性别
	 *
	 * @param idCard 身份编号
	 * @return 性别(M-男，F-女，N-未知)
	 */
	public static String getGenderByIdCard(String idCard) {
		String sGender = "未知";

		String sCardNum = idCard.substring(16, 17);
		if (Integer.parseInt(sCardNum) % 2 != 0) {
			sGender = "gender_1";// 男
		} else {
			sGender = "gender_2";// 女
		}
		return sGender;
	}

	public static boolean isLegalPattern(String identity) {
		String reg = "^\\d{15}$|^\\d{17}[0-9Xx]$";
		if (!identity.matches(reg)) {
			return false;
		}
		return true;
	}

	public static Integer getAge(String identityNumber) {
		// 截取出生年月日
		String birthday = identityNumber.substring(6, 14);
		// 当前日期
		String date = new SimpleDateFormat("yyyyMMdd").format(new Date());
		// 计算年龄差
		Integer ageBit = Integer.parseInt(date) - Integer.parseInt(birthday);
		// 当年龄差的长度大于4位时，即出生年份小于当前年份
		Integer personAge = 0;
		if (ageBit.toString().length() > 4) {
			// 截取掉后四位即为年龄
			personAge = Integer.parseInt(ageBit.toString().substring(0, ageBit.toString().length() - 4));
		}
		return personAge;
	}

	final static Map<Integer, String> zoneNum = new HashMap<Integer, String>();

	static {
		zoneNum.put(11, "北京");
		zoneNum.put(12, "天津");
		zoneNum.put(13, "河北");
		zoneNum.put(14, "山西");
		zoneNum.put(15, "内蒙古");
		zoneNum.put(21, "辽宁");
		zoneNum.put(22, "吉林");
		zoneNum.put(23, "黑龙江");
		zoneNum.put(31, "上海");
		zoneNum.put(32, "江苏");
		zoneNum.put(33, "浙江");
		zoneNum.put(34, "安徽");
		zoneNum.put(35, "福建");
		zoneNum.put(36, "江西");
		zoneNum.put(37, "山东");
		zoneNum.put(41, "河南");
		zoneNum.put(42, "湖北");
		zoneNum.put(43, "湖南");
		zoneNum.put(44, "广东");
		zoneNum.put(45, "广西");
		zoneNum.put(46, "海南");
		zoneNum.put(50, "重庆");
		zoneNum.put(51, "四川");
		zoneNum.put(52, "贵州");
		zoneNum.put(53, "云南");
		zoneNum.put(54, "西藏");
		zoneNum.put(61, "陕西");
		zoneNum.put(62, "甘肃");
		zoneNum.put(63, "青海");
		zoneNum.put(64, "宁夏");
		zoneNum.put(65, "新疆");
		zoneNum.put(71, "台湾");
		zoneNum.put(81, "香港");
		zoneNum.put(82, "澳门");
		zoneNum.put(91, "外国");
	}

	final static int[] PARITYBIT = { '1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2' };
	final static int[] POWER_LIST = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };

	/**
	 * 身份证验证
	 *
	 * @param certNo 号码内容
	 * @return 是否有效 null和"" 都是false
	 */
	public static boolean isIDCard(String certNo) {
		if (certNo == null || (certNo.length() != 15 && certNo.length() != 18))
			return false;
		final char[] cs = certNo.toUpperCase().toCharArray();
		// 校验位数
		int power = 0;
		for (int i = 0; i < cs.length; i++) {
			if (i == cs.length - 1 && cs[i] == 'X')
				break;// 最后一位可以 是X或x
			if (cs[i] < '0' || cs[i] > '9')
				return false;
			if (i < cs.length - 1) {
				power += (cs[i] - '0') * POWER_LIST[i];
			}
		}

		// 校验区位码
		if (!zoneNum.containsKey(Integer.valueOf(certNo.substring(0, 2)))) {
			return false;
		}

		// 校验年份
		String year = null;
		year = certNo.length() == 15 ? getIdcardCalendar(certNo) : certNo.substring(6, 10);

		final int iyear = Integer.parseInt(year);
		if (iyear < 1900 || iyear > Calendar.getInstance().get(Calendar.YEAR))
			return false;// 1900年的PASS，超过今年的PASS

		// 校验月份
		String month = certNo.length() == 15 ? certNo.substring(8, 10) : certNo.substring(10, 12);
		final int imonth = Integer.parseInt(month);
		if (imonth < 1 || imonth > 12) {
			return false;
		}

		// 校验天数
		String day = certNo.length() == 15 ? certNo.substring(10, 12) : certNo.substring(12, 14);
		final int iday = Integer.parseInt(day);
		if (iday < 1 || iday > 31)
			return false;

		// 校验"校验码"
		if (certNo.length() == 15)
			return true;
		return cs[cs.length - 1] == PARITYBIT[power % 11];
	}

	private static String getIdcardCalendar(String certNo) {
		// 获取出生年月日
		String birthday = certNo.substring(6, 12);
		SimpleDateFormat ft = new SimpleDateFormat("yyMMdd");
		Date birthdate = null;
		try {
			birthdate = ft.parse(birthday);
		} catch (java.text.ParseException e) {
			e.printStackTrace();
		}
		Calendar cday = Calendar.getInstance();
		cday.setTime(birthdate);
		String year = String.valueOf(cday.get(Calendar.YEAR));
		return year;
	}

	private static final Logger logger = LoggerFactory.getLogger(IDCardUtils.class);

	private static final String ENCODING = "UTF-8";

	//private static final String PASSWORD = "1#@234*56^&";

	private static final String PASSWORD = "idCardEncryption";

	/**
	 * 加密，把一个字符串在原有的基础上加上秘钥数字
	 * 
	 * @return 返回加密后的新字符串
	 */
	public static String encode(String content) {
//		// 把字符串转为字节数组
//		byte[] idCardBytes = idCard.getBytes();
//		// 遍历
//		for (int i = 0; i < idCardBytes.length; i++) {
//			// 在原有的基础上+秘钥数字
//			idCardBytes[i] += SECRET_NO;
//		}
//		return new String(idCardBytes);

		if (StringUtils.isEmpty(content)) {
			throw new RuntimeException("明文不能为空！");
		}
		byte[] encryptResult = encrypt(content);
		assert encryptResult != null;
		String encryptResultStr = parseByte2HexStr(encryptResult);
		// BASE64位加密
		encryptResultStr = ebotongEncrypto(encryptResultStr);
		return encryptResultStr;
	}

	/**
	 * 加密字符串
	 */
	private static String ebotongEncrypto(String str) {
		BASE64Encoder base64encoder = new BASE64Encoder();
		String result = str;
		if (str != null && str.length() > 0) {
			try {
				byte[] encodeByte = str.getBytes(ENCODING);
				result = base64encoder.encode(encodeByte);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		// base64加密超过一定长度会自动换行 需要去除换行符
		return result.replaceAll("\r\n", "").replaceAll("\r", "").replaceAll("\n", "");
	}

	/**
	 * 将二进制转换成16进制
	 *
	 * @param buf 2
	 * @return String
	 */
	private static String parseByte2HexStr(byte[] buf) {
		StringBuilder sb = new StringBuilder();
		for (byte b : buf) {
			String hex = Integer.toHexString(b & 0xFF);
			if (hex.length() == 1) {
				hex = '0' + hex;
			}
			sb.append(hex.toUpperCase());
		}
		return sb.toString();
	}

	/**
	 * 将16进制转换为二进制
	 *
	 * @param hexStr 16
	 * @return byte[]
	 */
	private static byte[] parseHexStr2Byte(String hexStr) {
		if (hexStr.length() < 1) {
			return null;
		}
		final int i1 = 2;
		byte[] result = new byte[hexStr.length() / i1];
		for (int i = 0; i < hexStr.length() / i1; i++) {
			int high = Integer.parseInt(hexStr.substring(i * i1, i * i1 + 1), 16);
			int low = Integer.parseInt(hexStr.substring(i * i1 + 1, i * i1 + i1), 16);
			result[i] = (byte) (high * 16 + low);
		}
		return result;
	}

	/**
	 * 加密
	 *
	 * @param content 需要加密的内容
	 * @return byte[]
	 */
	private static byte[] encrypt(String content) {
		try {
			KeyGenerator kgen = KeyGenerator.getInstance("AES");
			// 注意这句是关键，防止linux下 随机生成key。用其他方式在Windows上正常，但Linux上会有问题
			SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
			secureRandom.setSeed(IDCardUtils.PASSWORD.getBytes());
			kgen.init(128, secureRandom);
			SecretKey secretKey = kgen.generateKey();
			byte[] enCodeFormat = secretKey.getEncoded();
			SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
			// 创建密码器
			Cipher cipher = Cipher.getInstance("AES");
			byte[] byteContent = content.getBytes(StandardCharsets.UTF_8);
			// 初始化
			cipher.init(Cipher.ENCRYPT_MODE, key);
			// 加密
			return cipher.doFinal(byteContent);
		} catch (Exception e) {
			logger.error("加密异常", e);
		}
		return null;
	}

	/**
	 * 解密：将一个字符串在原有基础上减去秘钥数字
	 * 
	 * @return 返回解密后的新字符串
	 */
	public static String decode(String encryptResultStr) {
//		// 把字符串转为字节数组
//		byte[] idCardBytes = idCard.getBytes();
//		// 遍历
//		for (int i = 0; i < idCardBytes.length; i++) {
//			// 在原有的基础上-秘钥数字
//			idCardBytes[i] -= SECRET_NO;
//		}
//		return new String(idCardBytes);

		if (StringUtils.isEmpty(encryptResultStr)) {
			throw new RuntimeException("密文不能为空");
		}
		// BASE64位解密
		try {
			String decrpt = ebotongDecrypto(encryptResultStr);
			byte[] decryptFrom = parseHexStr2Byte(decrpt);
			byte[] decryptResult = decrypt(decryptFrom);
			assert decryptResult != null;
			return new String(decryptResult);
		} catch (Exception e) { // 当密文不规范时会报错，可忽略，但调用的地方需要考虑
			return null;
		}
	}

	/**
	 * 解密字符串
	 */
	private static String ebotongDecrypto(String str) {
		BASE64Decoder base64decoder = new BASE64Decoder();
		try {
			byte[] encodeByte = base64decoder.decodeBuffer(str);
			return new String(encodeByte);
		} catch (IOException e) {
			logger.error("IO 异常", e);
			return str;
		}
	}

	/**
	 * 解密
	 *
	 * @param content 待解密内容
	 * @return String
	 */
	private static byte[] decrypt(byte[] content) {
		try {
			KeyGenerator kgen = KeyGenerator.getInstance("AES");
			// 防止linux下 随机生成key
			SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
			secureRandom.setSeed(IDCardUtils.PASSWORD.getBytes());
			kgen.init(128, secureRandom);
			SecretKey secretKey = kgen.generateKey();
			byte[] enCodeFormat = secretKey.getEncoded();
			SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
			// 创建密码器
			Cipher cipher = Cipher.getInstance("AES");
			// 初始化
			cipher.init(Cipher.DECRYPT_MODE, key);
			// 加密
			return cipher.doFinal(content);
		} catch (Exception e) {
			logger.error("解密异常", e);
		}
		return null;
	}
}